/*
 * Created by SharpDevelop.
 * User: David
 * Date: 2006-11-26
 * Time: 23:30
 * 
 * To change this template use Tools | Options | Coding | Edit Standard Headers.
 */

using System;

using Labb4.Media.Graphics;

namespace Labb4.Entity
{
	/// <summary>
	/// This is the abstract super class for all entities. Enities are typically graphical representations
	/// of different things. Therefore this class has methods for moveing around things on a cartensian
	/// coordinate system.
	/// 
	/// Note: In C# the coordinate system is upside down.
	/// </summary>
	public abstract class BasicEntity
	{
		#region members
		
		/// <summary>
		/// The time in milliseconds that this entity is supposed to die.
		/// If this number is 0, the entity has eternal life, if positive, the entity will die
		/// when the entitymanager's clock passes that time
		/// </summary>
		private int					m_TimeToDie;
		/// <summary>
		/// This holds the current time of the entity. The entity's Counter is set to 0 when
		/// created, but can be reset to any given number when activated. The Entity is by default
		/// inactive when created, so to make this entity visible, it has to be activated after
		/// creation. If the field TimeToDie is supposed to work as expected, the activation
		/// method has to be called with the current time (according to the entitymanager) as parameter
		/// </summary>
		protected int				m_Counter;
		/// <summary>
		/// If this is 0, there is no delay, otherwise this is the number of milliseconds that will
		/// pass from the time the Activate-method was called until the entity actually goes active.
		/// </summary>
		private int					m_DelayedActivationTimer;
		/// <summary>
		/// If this entity is active, this field is true, otherwise this field is false
		/// </summary>
		protected bool				m_Active;
		/// <summary>
		/// If this entity is supposed to go active on a delay, this is true, otherwise this is false
		/// </summary>
		private bool				m_DelayedActivation;
		
		/// <summary>
		/// This is the exact x position of this entity in a cartesian coordinate system
		/// </summary>
		protected float				m_Xpos;
		/// <summary>
		/// This is the exact y position of this entity in un up side down cartesian coordinate system
		/// </summary>
		protected float				m_Ypos;
		/// <summary>
		/// This is the entity's width in pixels
		/// </summary>
		protected readonly int		m_Width;
		/// <summary>
		/// This is the entity's height in pixels
		/// </summary>
		protected readonly int		m_Height;
		/// <summary>
		/// The entity's speed, unit is (Pixels per second)
		/// </summary>
		protected float				m_Velocity;
		/// <summary>
		/// The entity's direction, unit is (radians)
		/// </summary>
		protected float				m_Direction;
		
		/// <summary>
		/// The picture of this graphical entity
		/// </summary>
		protected Picture			m_PictureData;
		/// <summary>
		/// This is the controler controling this entity, might be null
		/// </summary>
		protected Control			m_Controler;
		
		#endregion
		
		#region constructors
		
		/// <summary>
		/// Constructor of the basic entity. Sets members and registers the entity with the entity manager.
		/// Registering the entity means that the entity manager takes care of all the moving and otherwise
		/// updating of the entity. <see cref="EntityManager">See EntityManager class for more info on this.</see>
		/// </summary>
		/// <param name="x">The entity's starting x coordinate</param>
		/// <param name="y">The entity's starting y coordinate</param>
		/// <param name="width">The entity's width, used to determine if the entity "has" a certain point</param>
		/// <param name="height">The entity's height, used to determine if the entity "has" a certain point</param>
		/// <param name="velocity">The entity's velocity, used to determine the distance that the entity has traveled
		/// 						during a certain period of time. Unit is (pixels/sec)</param>
		/// <param name="direction">The entity's direction, used to determine the exact movement in x and y over the
		/// 						(upside down) cartesian coordinate system. Unit is (radians)</param>
		public BasicEntity(float x, float y, int width, int height, float velocity, float direction, Picture picture)
		{
			m_TimeToDie = 0;
			m_Counter = 0;
			m_DelayedActivationTimer = 0;
			m_Active = false;
			m_DelayedActivation = false;
			
			m_Xpos = x;
			m_Ypos = y;
			m_Width = width;
			m_Height = height;
			m_Velocity = velocity;
			m_Direction = direction;
			
			m_PictureData = picture;
			m_Controler = null;
			
			EntityManager.Instance.AddEntity(this);
		}
		
		#endregion
		
		#region methods
		
		/// <summary>
		/// Used to activate the entity. An inactive entity is not updated or drawn to the screen.
		/// </summary>
		/// <param name="currentTime">Sets the Counter. Unit is (ms)</param>
		public void Activate(int currentTime)
		{
			m_Active = true;
			m_Counter = currentTime;
		}
		
		/// <summary>
		/// Used to activate the entity. An inactive entity is not updated or drawn to the screen.
		/// The activation is however delayed by a certain period of time.
		/// </summary>
		/// <param name="currentTime">Sets the Counter. Unit is (ms)</param>
		/// <param name="delay">Sets the period of time that the activation is delayed. Unit is (ms)</param>
		public void Activate(int currentTime, int delay)
		{
			m_DelayedActivation = true;
			m_Counter = currentTime;
			m_DelayedActivationTimer = delay;
		}
		
		/// <summary>
		/// Deactivates the entity, making it disappear from the screen, and stop. It will not be
		/// updated any more. The entity does not die though, so it can be reactivated again at any
		/// time.
		/// </summary>
		public void Deactivate()
		{
			m_Active = false;
			m_DelayedActivation = false;
			m_Counter = 0;
			m_DelayedActivationTimer = 0;
		}
		
		/// <summary>
		/// This method checks to see if the entity has a certain point. It simply checks the square
		/// that's made up from the (x, y) position and the width and height of the entity.
		/// </summary>
		/// <param name="x">The x coordinate to check</param>
		/// <param name="y">The y coordinate to check</param>
		/// <returns>True if the point is within the area of this entity, otherwise false</returns>
		public bool HasPoint(ref int x, ref int y)
		{
			bool hasPoint = false;
			
			if(m_Active)
			{
				hasPoint = true;
				
				if(x < m_Xpos)
				{
					hasPoint = false;
				}
				
				if(x > (m_Xpos + m_Width - 1))
				{
					hasPoint = false;
				}
				
				if(y < m_Ypos)
				{
					hasPoint = false;
				}
				
				if(y > (m_Ypos + m_Height - 1))
				{
					hasPoint = false;
				}
			}
			
			return hasPoint;
		}
		
		#endregion
		
		#region virtuals
		
		/// <summary>
		/// This method adds an animation to an entity that has animations (typically a sprite entity).
		/// </summary>
		/// <param name="name">The name of the animation, this is then used to select the animation. Each name must be unique.</param>
		/// <param name="animation">The animation to associate with the name.</param>
		public virtual void AddAnimation(string name, Animation animation) {}
		/// <summary>
		/// Selects the current animation. The animation is added to the queue of animations ready to play.
		/// If an animation is a <see cref="Animation">"must finish" animation</see> all the queued animations
		/// will play once, one at a time.
		/// </summary>
		/// <param name="name">The name of the animation.</param>
		public virtual void SelectAnimation(string name) {}
		/// <summary>
		/// Selects the current animation. The animation is added to the queue of animations ready to play.
		/// If an animation is a <see cref="Animation">"must finish" animation</see> all the queued animations
		/// will play once, one at a time.
		/// </summary>
		/// <param name="i">The number of the animation, corresponding to the order in which the animations were added.</param>
		public virtual void SelectAnimation(int i) {}
		
		/// <summary>
		/// Kills the entity. If the entity has the requested death animation, the entity will live for
		/// the amount of time it takes to play that animation once. Otherwise it will die emidietely.
		/// The entity is updated as usual while dying, so if it has a velocity, it will keep moving while
		/// it's dying.
		/// </summary>
		/// <param name="deathAnimation">The name of the animation to play while dying.</param>
		public virtual void Die(string deathAnimation)
		{
			m_TimeToDie = EntityManager.Instance.CurrentTime;
		}
		
		/// <summary>
		/// This is where the entity is updated, and thus moved if it has a velocity. All updating is
		/// done in reference to the time passed since the last update. This means that used correctly,
		/// updating becomes independant from fps.
		/// 
		/// Update only works if the entity is active.
		/// </summary>
		/// <param name="deltaTime">The time passed since the last update. Unit is (ms)</param>
		public virtual void Update(ref int deltaTime)
		{
			if(m_DelayedActivation)
			{
				m_Counter += deltaTime;
				
				if(m_Counter > m_DelayedActivationTimer)
				{
					m_Active = true;
					m_DelayedActivation = false;
				}
			}
			
			if(m_Active)
			{
				m_Counter += deltaTime;
				m_Xpos += ((float)Math.Cos((double)m_Direction) * m_Velocity * (float)deltaTime / 1000.0f);
				m_Ypos += ((float)Math.Sin((double)m_Direction) * m_Velocity * (float)deltaTime / 1000.0f);
			}
		}
		
		#endregion
		
		#region abstracts
		
		/// <summary>
		/// This method should be implemented to check the graphical represenation to see if the
		/// point asked is a color or not. Not being a color, means that the color at the asked point
		/// is the color that has been set to transparent. Implementation is very different in
		/// a picture entity from that of a sprite entity.
		/// </summary>
		/// <param name="x">The x coordinate to check</param>
		/// <param name="y">The y coordinate to check</param>
		/// <returns>True if the point asked for is color, otherwise false</returns>
		public abstract bool PointIsColor(ref int x, ref int y);
		/// <summary>
		/// Draws the graphical representation of the entity. Implementation is very different in
		/// a picture entity from that of a sprite entity.
		/// </summary>
		public abstract void Draw();
		
		#endregion
		
		#region properties
		
		/// <summary>
		/// The entity's x coordinate. Unit is (pixel) (set/get)
		/// </summary>
		public float Xpos
		{
			get
			{
				return m_Xpos;
			}
			
			set
			{
				m_Xpos = value;
			}
		}
		
		/// <summary>
		/// The entity's y coordinate. Unit is (pixel) (set/get)
		/// </summary>
		public float Ypos
		{
			get
			{
				return m_Ypos;
			}
			
			set
			{
				m_Ypos = value;
			}
		}
		
		/// <summary>
		/// The entity's width. Unit is (pixel) (get)
		/// </summary>
		public int Width
		{
			get
			{
				return m_Width;
			}
		}
		
		/// <summary>
		/// The entity's height. Unit is (pixel) (get)
		/// </summary>
		public int Height
		{
			get
			{
				return m_Height;
			}
		}
		
		/// <summary>
		/// The entity's velocity. Unit is (pixels/sec) (set/get)
		/// </summary>
		public float Velocity
		{
			get
			{
				return m_Velocity;
			}
			
			set
			{
				m_Velocity = value;
			}
		}
		
		/// <summary>
		/// The entity's direction. Unit is (radians) (set/get)
		/// </summary>
		public float Direction
		{
			get
			{
				return m_Direction;
			}
			
			set
			{
				m_Direction = value;
			}
		}
		
		/// <summary>
		/// The entity's death time. Unit is (ms) (set/get)
		/// </summary>
		public int TimeToDie
		{
			get
			{
				return m_TimeToDie;
			}
			
			set
			{
				m_TimeToDie = value;
			}
		}
		
		/// <summary>
		/// The entity's controler. When setting, the controler is automatically
		/// assigned this entity as its entity (set/get)
		/// </summary>
		public Control Controler
		{
			get
			{
				return m_Controler;
			}
			
			set
			{
				m_Controler = value;
				m_Controler.Entity = this;
			}
		}
		
		/// <summary>
		/// The entity's current active status (get)
		/// </summary>
		public bool Active
		{
			get
			{
				return m_Active;
			}
		}
		
		#endregion
	}
}
